package com.arelikebrothers.demo.algorithm.basic.analysis;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by lennon on 16/06/2017.
 */
public class Calc {
    private Pattern numberPattern = Pattern.compile("[0-9]+|([0-9]+.[0-9]+)");

    public double calc(String expres) {

        List<String> items = splitExpress(expres);

        List<String> postfix = toPostfix(items);

        double value = calcPostfix(postfix);
        return value;
    }

    private double calcPostfix(List<String> postfix) {
        Stack<Double> stack = new Stack<>();
        postfix.forEach(item -> {
            if (isNum(item)) {
                stack.push(Double.valueOf(item));
            } else {
                double one = stack.pop();
                double another = stack.pop();
                double ret = doCalc(another, one, item);
                stack.push(ret);
            }
        });

        return stack.pop();
    }

    private double doCalc(double one, double another, String opt) {
        switch (opt) {
            case "+":
                return one + another;
            case "-":
                return one - another;
            case "*":
                return one * another;
            case "/":
                return one / another;
        }
        return 0;
    }

    /**
     * 中缀表达式转换成后缀表达式
     *
     * @param items
     * @return
     */
    private List<String> toPostfix(List<String> items) {
        List<String> postfix = new ArrayList<>();

        Stack<String> optStack = new Stack<>();

        items.forEach(item -> {
            if (isNum(item)) {
                postfix.add(item);
            } else {
                if (optStack.empty() || "(".equals(item)) {
                    optStack.push(item);
                } else if (")".equals(item)) {
                    while (!optStack.empty()) {
                        String o = optStack.pop();
                        if ("(".equals(o)) {
                            break;
                        }
                        postfix.add(o);
                    }
                } else {
                    while (!optStack.empty()) {
                        String topOpt = optStack.peek();
                        if (compareOpt(item, topOpt)) {
                            break;
                        } else {
                            String o = optStack.pop();
                            postfix.add(o);
                        }
                    }
                    optStack.push(item);
                }
            }
        });
        String opt = null;
        while (!optStack.empty()) {
            postfix.add(optStack.pop());
        }
        System.out.println(postfix);

        return postfix;

    }

    private boolean compareOpt(String item, String topOpt) {
        if (topOpt == null) {
            return false;
        }

        if ("(".equals(topOpt)) {
            return true;
        } else if ("*".equals(item) || "/".equals(item)) {
            if ("*".equals(topOpt) || "/".equals(topOpt)) {
                return false;
            }
            return true;
        } else if ("+".equals(item) || "-".equals(item)) {
            return false;
        }
        return false;
    }

    private boolean isNum(String in) {
        if (numberPattern.matcher(in).matches()) {
            return true;
        }
        return false;
    }

    /**
     * 将表达式切割成分析项
     *
     * @param expres
     * @return
     */
    private List<String> splitExpress(String expres) {
        Pattern pattern = Pattern.compile("([0-9.]+)|([+\\-*/()])");
        Matcher matcher = pattern.matcher(expres);
        List<String> items = new ArrayList<>();

        while (matcher.find()) {
            items.add(matcher.group(0));
        }
        System.out.println(items);
        return items;
    }

    public static void main(String[] args) {
        Calc calc = new Calc();
//        System.out.println(calc.calc("1+2*3+(4*5+6)*7"));
        System.out.println(calc.calc("3+5*6/3*(1+2)-4"));

    }

}
